﻿using System;
using System.Windows.Forms;
using OpenNETCF.Drawing.Imaging;
using OpenNETCF.IoC;
using ProjectResistance.Shell.Controllers;
using System.Text;
using System.Collections.Generic;
using System.Drawing;
using OpenNETCF.Drawing;
using System.Linq;
using System.Diagnostics;
using System.IO;

namespace ProjectResistance.Shell.Views
{
    /// <summary>
    /// Used to display a graphical representation of a resistor
    /// </summary>
    internal partial class ResistorView : ViewBase
    {
        private class BandInfo
        {
            public Rectangle Bounds { get; set; }
            public BandColor Color { get; set; }
            public int Index { get; set; }
        }

        // TODO: adjust these for actual alignment
        private const int BAND_IMAGE_HEIGHT = 60;
        private const int BAND_IMAGE_TOP = 2;
        private const int BAND_IMAGE_SPACING = 35;
        private const int BAND_IMAGE_WIDTH1 = 23;
        private const int BAND_IMAGE_LEFT1 = 58;
        private const int BAND_IMAGE_WIDTH2 = 21;
        private const int BAND_IMAGE_LEFT2 = BAND_IMAGE_LEFT1 + BAND_IMAGE_SPACING;
        private const int BAND_IMAGE_WIDTH3 = 19;
        private const int BAND_IMAGE_LEFT3 = BAND_IMAGE_LEFT2 + BAND_IMAGE_SPACING;
        private const int BAND_IMAGE_WIDTH4 = 23;
        private const int BAND_IMAGE_LEFT4 = BAND_IMAGE_LEFT3 + BAND_IMAGE_SPACING;

        private const int TOLERANCE_BAND = 4;

        private Bitmap m_blankImage;
        private Bitmap m_bandsImage;
        private ImagingFactoryClass m_factory;

        private ResistorController Controller { get; set; }
        private List<BandInfo> Bands { get; set; }
        private BandInfo GestureBand { get; set; }
        private Point LastMouseDown { get; set; }

        [InjectionConstructor]
        public ResistorView([ServiceDependency(EnsureExists = true)] ResistorController controller)
        {
            InitializeComponent();

            Controller = controller;
            Controller.ResistanceChanged += new EventHandler<GenericEventArgs<decimal>>(Controller_ResistanceChanged);
            Controller.SelectedBandChanged += new EventHandler<GenericEventArgs<int>>(Controller_SelectedBandChanged);
            
            m_factory = new ImagingFactoryClass();
            StreamOnFile sof = new StreamOnFile(GetType().Assembly.GetManifestResourceStream("ProjectResistance.Shell.Parts.Blank.png"));
            IImage imgBlank;
            m_factory.CreateImageFromStream(sof, out imgBlank);
            ImageInfo ii;
            imgBlank.GetImageInfo(out ii);
            m_blankImage = new Bitmap((int)ii.Width, (int)ii.Height, (System.Drawing.Imaging.PixelFormat)Constants.PixelFormat.F32bppARGB);
            Infrastructure.GraphicTools.TransparentClearBitmap(m_blankImage);
            using (Graphics g = Graphics.FromImage(m_blankImage))
            {
                IntPtr hdc = g.GetHdc();
                imgBlank.Draw(hdc, new RECT(0, 0, m_blankImage.Width, m_blankImage.Height), null);
                g.ReleaseHdc(hdc);
            }

            Bands = new List<BandInfo>(new BandInfo[] 
            { 
                new BandInfo { Index = 1, Color = BandColor.Black, Bounds = new Rectangle(BAND_IMAGE_LEFT1, BAND_IMAGE_TOP, BAND_IMAGE_WIDTH1, BAND_IMAGE_HEIGHT) },
                new BandInfo { Index = 2, Color = BandColor.Black, Bounds = new Rectangle(BAND_IMAGE_LEFT2, BAND_IMAGE_TOP, BAND_IMAGE_WIDTH2, BAND_IMAGE_HEIGHT) },
                new BandInfo { Index = 3, Color = BandColor.Black, Bounds = new Rectangle(BAND_IMAGE_LEFT3, BAND_IMAGE_TOP, BAND_IMAGE_WIDTH3, BAND_IMAGE_HEIGHT) },
                new BandInfo { Index = 4, Color = BandColor.Red, Bounds = new Rectangle(BAND_IMAGE_LEFT4, BAND_IMAGE_TOP, BAND_IMAGE_WIDTH4, BAND_IMAGE_HEIGHT) },
            });

            Controller.SetResistance(47000);
            Controller.SetTolerance(ToleranceValues.OnePercent);

            // add this last so we don't get an event for both the resistance and tolerance initialization
            Controller.ToleranceChanged += new EventHandler<GenericEventArgs<ToleranceValues>>(Controller_ToleranceChanged);
        }

        void Controller_SelectedBandChanged(object sender, GenericEventArgs<int> e)
        {
            Debug.WriteLine("Band selection changed to: " + e.Value.ToString());

            // force a repaint so the selection gets updated
            Refresh();
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            LastMouseDown = new Point(e.X, e.Y);

            // is it in a band?
            GestureBand = (from b in Bands
                         where b.Bounds.Contains(LastMouseDown)
                         select b).FirstOrDefault();

            base.OnMouseDown(e);
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            // were we in a band to start with?
            if (GestureBand != null)
            {
                // find gesture validity (height > width == gesture)
                int dX = e.X - LastMouseDown.X;
                int dY = e.Y - LastMouseDown.Y;

                if (Math.Abs(dY) > Math.Abs(dX))
                {
                    // TODO: raise gesture event
                    if (LastMouseDown.Y < e.Y)
                    {
                        if (GestureBand.Index == TOLERANCE_BAND)
                        {
                            Controller.ToleranceDown();
                        }
                        else
                        {
                            Controller.ResistanceDown(GestureBand.Index);
                        }
                    }
                    else
                    {
                        if (GestureBand.Index == TOLERANCE_BAND)
                        {
                            Controller.ToleranceUp();
                        }
                        else
                        {
                            Controller.ResistanceUp(GestureBand.Index);
                        }
                    }
                }
            }

            base.OnMouseUp(e);
        }

        private Bitmap CreateBandsImage(BandInfo[] bands)
        {
            if (bands == null) throw new ArgumentNullException();
            if ((bands.Length < 3) || (bands.Length > 4)) throw new ArgumentOutOfRangeException();

            List<StreamOnFile> sofs = new List<StreamOnFile>();
            sofs.Add(new StreamOnFile(GetImageStream(bands[0].Color, 1)));
            sofs.Add(new StreamOnFile(GetImageStream(bands[1].Color, 2)));
            sofs.Add(new StreamOnFile(GetImageStream(bands[2].Color, 3)));
            sofs.Add(new StreamOnFile(GetImageStream(bands[3].Color, 4)));

            Bitmap bmp = new Bitmap(this.Width, this.Height, (System.Drawing.Imaging.PixelFormat)Constants.PixelFormat.F32bppARGB);
            Infrastructure.GraphicTools.TransparentClearBitmap(bmp);
            Graphics g = Graphics.FromImage(bmp);
            IntPtr hdc = g.GetHdc();
            try
            {
                int left = BAND_IMAGE_LEFT1;
                int top = BAND_IMAGE_TOP;
                int right = left + BAND_IMAGE_WIDTH1;
                int bottom = top + BAND_IMAGE_HEIGHT;

                for (int i = 0; i < sofs.Count; i++)
                {
                    IImage img;
                    m_factory.CreateImageFromStream(sofs[i], out img);

                    img.Draw(hdc, new RECT(left, top, right, bottom), null);

                    left += BAND_IMAGE_SPACING;

                    switch (i)
                    {
                        case 0:
                            right = left + BAND_IMAGE_WIDTH1;
                            break;
                        case 1:
                            right = left + BAND_IMAGE_WIDTH2;
                            break;
                        case 2:
                            right = left + BAND_IMAGE_WIDTH3;
                            break;
                        case 3:
                            right = left + BAND_IMAGE_WIDTH4;
                            break;
                    }
                }
            }
            finally
            {
                g.ReleaseHdc(hdc);
            }

            return bmp;
        }

        private Stream GetImageStream(BandColor color, int index)
        {
            Stream s = GetType().Assembly.GetManifestResourceStream(BandToFileName(color, index));
            if (s == null)
            {
                // not found!
                if (Debugger.IsAttached) Debugger.Break();
            }
            return s;
        }

        private string BandToFileName(BandColor color, int position)
        {
            if((position < 1) || (position > 4)) throw new ArgumentOutOfRangeException();

            return string.Format("ProjectResistance.Shell.Parts.Ring{0}.{1}.png", position, color.ToString().ToLower());
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            try
            {
                RECT rect = new RECT(this.Bounds);

                // draw the blank
                Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_blankImage, Bounds, new Rectangle(0, 0, m_blankImage.Width, m_blankImage.Height));

                if (m_bandsImage != null)
                {
                    // draw the bands
                    Infrastructure.GraphicTools.DrawTransparentBitmap(e.Graphics, m_bandsImage, Bounds, new Rectangle(0, 0, m_bandsImage.Width, m_bandsImage.Height));
                }
            }
            finally
            {
            }

            if (!Controller.TouchMode)
            {
                // TODO: draw in the selection arrow
                // Controller.SelectedBand
            }
        }

        private void UpdateBands(decimal resistance, ToleranceValues tolerance)
        {
            BandColor[] colors = Utility.GetBands(resistance, tolerance);

            for (int i = 0; i < 4; i++)
            {
                Bands[i].Color = colors[i];
            }

            m_bandsImage = CreateBandsImage(Bands.ToArray());
            this.Refresh();
        }

        private void Controller_ResistanceChanged(object sender, GenericEventArgs<decimal> e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new EventHandler<GenericEventArgs<decimal>>(Controller_ResistanceChanged), 
                    new object[] { sender, e });

                return;
            }

            UpdateBands(e.Value, Controller.GetTolerance());
        }

        private void Controller_ToleranceChanged(object sender, GenericEventArgs<ToleranceValues> e)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new EventHandler<GenericEventArgs<ToleranceValues>>(Controller_ToleranceChanged),
                    new object[] { sender, e });

                return;
            }

            UpdateBands(Controller.GetResistance(), e.Value);
        }

    }
}
